#!/usr/bin/env python3

import pathlib
class Abs_path:
    @staticmethod
    def exec_cwd():
        import os
        return os.getcwd()
    @staticmethod
    def exec_name():
        import os, sys
        return Abs_path.dir_and_name("/"+sys.argv[0])[1]
    @staticmethod
    def exec_path():
        return Abs_path.abs_path(Abs_path.exec_cwd(), Abs_path.exec_name())
    @staticmethod
    def dir_and_name(abs_path):
        if abs_path == "" or abs_path[0] != "/": 
            raise Exception("path is not abs_path")
        if abs_path == "/": 
            raise Exception("root dir no parent")
        dir_, name = abs_path.rsplit("/", 1)
        if dir_ == "": 
            dir_ = "/"
        return dir_, name
    def name(relate_or_abs_path):
        p = relate_or_abs_path
        return p if "/" not in p else p.rsplit("/", 1)[1]
    def abs_path(abs_dir, name):
        return abs_dir + "/" + name

class Dir:
    def __init__(self, abs_dir=None):
        self.abs_dir = abs_dir if abs_dir!=None else Abs_path.exec_cwd()
        if self.abs_dir[0] != "/":
            print(abs_dir)
            raise Exception("not abs dir!!")
    def __str__(self): return self.abs_dir
    def s(self): return self.__str__()

    def Path(self): return pathlib.Path(self.abs_dir)
    def glob(self, fmt):
        #path_list =  self.Path().glob(fmt)
        path_list =  self.Path().glob(fmt)
        
        # path_list = list(path_list)
        # path_list = list(sorted( path_list ))
        r = [str(p) for p in path_list]
        # for p in r:
        #     print(p)
        return r
    @staticmethod
    def exec_file(): return Dir(Abs_path.exec_path())

    def dir_and_name(self):
        return Abs_path.dir_and_name(self.abs_dir)
    def name(self): return self.dir_and_name()[1]

    def __truediv__(self, that):
        return Dir(self.abs_dir + "/" + that)
    # def __lt__(self, num):
    #     t = self.abs_dir.split("/")
    #     if len(t) <= num + 1: raise Exception("root have no father!")
    #     return Dir("/".join(t[:-num]))
    def is_dir(self):
        return pathlib.Path(self.abs_dir).is_dir()
    def exists(self):
        return pathlib.Path(self.abs_dir).exists()
    def mkdir(self):
        pathlib.Path(self.abs_dir).mkdir(exist_ok=True)
    def write(self, text):
        if self.is_dir():
            #print(self.exists())
            raise Exception("{this_file} not a file!".format(this_file=self.s()))
        pathlib.Path(self.abs_dir).open("w").write(text)
    def read(self):
        if not self.exists() or self.is_dir():
            raise Exception("{} not a file!".format(self.abs_dir))
        return pathlib.Path(self.abs_dir).open("r").read()
    def rmdir(self):
        pathlib.Path(self.abs_dir).mkdir()
    def clear_dir(self):
        self.rmdir()
        self.mkdir()
    def make_tree(self, tree_dict):
        self.mkdir()
        # print(tree_dict)
        for file in tree_dict:
            file_text = tree_dict[file]
            if isinstance(file_text, str):
                # print("str")
                # print(file)
                (self / file).write(file_text)
            else:
                # print("else")
                # print(file)
                (self / file).make_tree(file_text)
    def rm(self):
        self.Path().unlink()


class Dir_tree:
    def __init__(self, abs_path=None, text_or_dict=None):
        # abs_path = 
        # 1 abs_path
        # 2 None
        # 3 __file__
        # 4 file under exec_file_dir
        abs_path = Dir_tree.file_path(abs_path)
        self.d = Dir(abs_path)
        self.text = text_or_dict# test == {} or text == ""
    
    # def __lt__(self, text):
    #     if isinstance( that, str):
    #         self.text = that
    #     else:
    #         raise Exception("type error")
    #find and insert
    # def __getitem__(self, key):
    #     # if isinstance(key, slice):
    #     #     return [self.list[i] for i in xrange(key.start, key.stop, key.step)]
    #     if self.text == None:
    #         self.text = {}
    #     if key not in self.text:
    #         self.text[key] = Dir_tree( str(self.d/key) )
    #    return self.text[key]
    @staticmethod
    def exe_file_path():
        import sys
        return Dir_tree.file_path(sys.argv[0])
    @staticmethod
    def exe_file_cwd():
        import os
        return os.getcwd()
    @staticmethod
    def file_path(_file_):
        if _file_ == None: 
            return Dir_tree.file_cwd(_file_)
        return Dir_tree.file_cwd(_file_) + "/" + Dir_tree.file_name(_file_)
    @staticmethod
    def file_cwd(_file_):
        if _file_ == None or _file_[0] != "/":
            return Dir_tree.exe_file_cwd()
        else: 
            return  _file_.rsplit("/", 1)[0]
    @staticmethod
    def file_name(_file_):
        return _file_.rsplit("/", 1)[1] if "/" in _file_ else _file_
    def lib_dir(): 
        return ".."/Dir_tree(__file__)
    def exe(): return Dir_tree(Dir_tree.exec_file_path())
    
    def path(self): return self.d.abs_dir
    def cwd(self): return self.path().rsplit('/', 1)[0]
    def name(self): return self.path().rsplit('/', 1)[1]
    def stem(self): return self.name().rsplit(".", 1)[0]
    def suffix(self): return self.name().rsplit(".", 1)[1]

    def exists(self): return self.d.exists()
    def is_dir(self): return self.d.is_dir()
    def is_file(self): return self.exists() and self.not_dir()
    def not_dir(self): return not self.d.is_dir()
    def set_text(self, text):
        self.text = text
        self.check()
    def check(self):
        if self.text == None:
            return True
        if self.exists():
            if isinstance(self.text, dict) and self.not_dir(): 
                raise Exception("check error: self.text is dict and path {} is not dir".format(self.path()))
            if isinstance(self.text, str) and self.is_dir():
                raise Exception("check error: self.text is str and path {} is dir".format(self.path()))
            #raise Exception("type error")
    
    def __truediv__(self, sub_file):
        if "/" in sub_file:
            sub_file_1, sub_file_rest = sub_file.split("/", 1)
            return self / sub_file_1/ sub_file_rest
        if not isinstance(sub_file, str):
            sub_file_type = type(sub_file)
            raise Exception("type error:  sub_file [{sub_file}] <{sub_file_type}> not a str".format(**locals() ))
        # not dict
        if self.text == None:
            t = {}
            self.set_text(t)
        if isinstance(self.text, str):
            raise Exception("type error self.text {} is a str".format(self.text))

        # is dict
        if sub_file not in self.text:
            self.text[sub_file] = Dir_tree((self.d/sub_file).abs_dir)
        self.d.mkdir()
        #print(sub_file)
        return self.text[sub_file]
        #return self.text[key]
    def __rtruediv__(self, back):
        if isinstance(back, str):
            back_list = back.split("/")
            cur_d = self
            for s in back_list:
                if s != "..": raise Exception("back is not legal")
            for i in range(len(back_list)):
                father_path, cur_name = self.path().rsplit("/", 1)
                father = Dir_tree(father_path)
                text = {cur_name:cur_d}
                father.set_text(text)
                cur_d = father
            return cur_d
    # def __eq__(self, that):
    #     if isinstance( that, Dir_tree):
    #         self.d = Dir(that.d.s())
    #         self.text = that.text
    #     else:
    #         raise Exception("type error")
    def __lshift__(self, file_text):
        if isinstance(file_text, list):
            file_text = "\n".join(file_text)
        self.write(file_text)
    def __add__(self, append_text):
        if isinstance(append_text, list):
            append_text = "\n".join(append_text)
        #append_text = "\n" + append_text
        self.write(append_text, opt="a+")
        return self
    def write(self, text=None, opt="w+"):
        if text != None:
            self.set_text(text)
        print("write "+ self.path())
        with open(self.path(), opt) as f:
            f.write(self.text+"\n")
        #self.d.Path().open(opt).write(self.text)
    def __str__(self):
        return self.d.read()
    def __iter__(self):
        if not isinstance(self.text, dict):
            for line in str(self).splitlines():
                yield line
        else:
            for sub_file in self.text.values():
                yield sub_file
        #int.__de
    # def __gt__(self, list_buf):
    #     list_buf.clear()
    #     list_buf.append(self.d.read())
    # def __rshift__(self, list_buf):
    #     self.__gt__(list_buf)
    #     list_buf = list_buf[0].splitlines()
    # def rm(self, file=None):
    #     t = self
    #     if file != None:
    #         t.mkdir()
    #         t = t/file
    #     if t.is_dir():
    #         print("rmdir "+d.path())
    #         t.rmdir()
    #     else:
    #         print("rm "+d.path())
    #         t.rm()
    def move(self, that_path):
        # https://docs.python.org/3/library/shutil.html#shutil.rmtree
        import shutil
        if self.exists():
            # f_sub = self/sub_file
            # if f_sub.exists():
            shutil.move( self.path(), that_path)
    def recycle_path():
        return (Dir_tree.lib_dir()/"recycle").path()
    def recycle_path_clear():
        print("clear")
        print(Dir_tree.recycle_path())
        import os
        os.system("sudo rm -rf {}/*".format(Dir_tree.recycle_path()))
    def recycle_path_now():
        from datetime import datetime
        now = str(datetime.now())
        now = "".join("_".join(now.split(" ")).split(":"))
        return (Dir_tree.lib_dir()/"recycle"/now).path()
    def rm(self, sub=None):
        rm_d = self
        if sub != None:
            rm_d = rm_d / sub
        print("rm "+ rm_d.path())
        rm_d.move(Dir_tree.recycle_path_now())
    def mkdir(self): self.d.mkdir() 
    def rmdir(self): self.d.rmdir()
    def glob(self, fmt):
        #path_list =  self.Path().glob(fmt)
        path_list =  self.d.glob(fmt)
        
        # path_list = list(path_list)
        # path_list = list(sorted( path_list ))
        r = [Dir_tree(str(p)) for p in path_list]
        return r


    #find 
    # def has(self, son_name):
    #     if isinstance( self.text, str):
    #         return False
    #     if self.text == None:
    #         return False
    #     if son_name in self.text:
    #         return True
    #     else:
    #         return False
    

    def show(self):  
        r =  Dict.json_str(self.text, "")
        if ":" in r:
            r = r.split(":", 1)[1].strip()
        return r

    def read_all(self, dir_only=False):
        if not self.d.exists():
            raise Exception("no such file or directory.")
        if not self.d.is_dir():
            if not dir_only:
                self.text = self.d.read()
        else:
            self.text = {}
            for abs_dir in self.d.glob("*"):#[abs_path.rsplit("/", 1)[1] for abs_path in self.d.glob("*")]:
                sub_d = Dir_tree( abs_dir )
                self.text[sub_d.name()] = sub_d
                self.text[sub_d.name()].read(dir_only=dir_only)
        return self.text
    def write_all(self, text=None):
        if text != None:
            self.set_text(text)
        if isinstance(self.text, str):
            self.d.write(self.text)
        elif isinstance(self.text, dict):
            for k in self.text:
                self.text[k].write_all()
        else:
            raise Exception("type error")


if __name__ == "__main__":



    d = Dir_tree()

    d.rm("123")
    # mkdir if not exist
    d/"123"/'abc'
    # write file w+
    d/"123"/"test.py" << 'print("hello world!")'
    # write file append   a+
    d/"123"/"test.py" + ";"

    # read file
    # read file as string in list
    buf = str(d/"123"/"test.py")# > str
    print(buf)
    # read file as line in list
    buf = list(d/"123"/"test.py")# > list
    print(buf)

    # get path
    abs_path = d.path()
    name = d.name()

    # create father
    father = ".."/d
    grand_father = "../.."/d

    print(father.path())
    print(grand_father.path())

    
    # exist, 
    d/"123"/"444" + "??"
    if (d/"123").is_dir() and "444" in d/"123" and (d/"123"/"444").not_dir():
        d/"123"/"444" << "ext_text"
    else:
        print((d/"123").is_dir())
        print("444" in d/"123")
        print((d/"123"/"444").not_dir())
    d123 = d/"123"
    d123/"to_rm" << "hello "
    d123/"to_rm" + "123"
    to_rm = d123/"to_rm"
    print(list(to_rm))
    d123.rm("to_rm")
    #print(list(to_rm))
    # ls.
    if 1:
        for sub_d in (d/"123").glob("*"):
            if sub_d.not_dir():
                print("====file====" + sub_d.path())
                for line in list(sub_d):
                    print(line)
    


    if 0:
        (d/"123"/"inc"/"Array.h").todo("#include <stdio.h>")
        (d/"123"/"src"/"Array.c").todo("""int main(){printf("hello world!\n")}""")
        d.write_all()
    if 0:
        r = d/"123".shell("gcc main.c Array.c; a.out")
        print(r)
    # print(Dict.json_str({
    #     "k0": {
    #         "k0-0":[
    #             {
    #                 "k0-0-0": "k0-0-0-0"
    #             }
    #         ]
    #     },
    #     "k1": "str",
    #     "k2": [
    #         "e1",
    #         "e2",
    #         "e3"
    #     ],
    #     "k3": {
    #         "k1": "v1",
    #         "k2": "v2",
    #         "k3": "v3"
    #     },
    #     "k4": None, 
    #     None: "v5",
    #     None: None
    # }, "dict"))
